package org.shoukaiseki.common.utils;

import cn.hutool.core.convert.Convert;
import org.apache.commons.lang3.time.DateUtils;
import org.shoukaiseki.kakaroto.Interface.DictTypeEnumData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class NumberUtils {

    public static final Logger logger = LoggerFactory.getLogger(NumberUtils.class);

    /**
     * <pre>
     * 排序置顶按照当前时间戳增加一年
     * 保证新添加的不会顶掉之前置顶的排序
     * </pre>
     */
    public static final long SORT_TOP_VALUE_ADDED=DateUtils.MILLIS_PER_DAY*365;

    public static final Long LONG_ZERO=0L;

    public static final Long LONG_ONE=1L;


    public static final Integer INTGER_ONE=1;

    public static final Integer INTGER_ZERO=0;

    /**
     * 允许的 sort 最小排序值
     */
    public static final long  MIN_SORT_VALUE=1234567890L;

    private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);

    private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);



    /**
     * 取反
     * @param a
     * @return      如果a为null则返回null,否则返回取反数
     */
    public static <T extends Number> T negateHasNull(Number a){
        if(a==null){
            return null;
        }
        if(a instanceof BigDecimal){
            return (T) ((BigDecimal) a).negate();
        }
        if(a instanceof Long){
            return (T) Long.valueOf((0L - a.longValue()));
        }
        if(a instanceof Integer){
           return (T)Integer.valueOf(0-a.intValue());
        }
        if(a instanceof Short){
            return (T)Short.valueOf((short) (0-a.shortValue()));
        }
        return (T)new BigDecimal(a.toString()).negate();
    }

    /**
     * 绝对值
     * @param a
     * @return      如果a为null则返回null,否则返回取反数
     */
    public static <T extends Number> T absHasNull(Number a){
        if(a==null){
            return null;
        }
        BigDecimal val = ((BigDecimal) a).abs();
        if(a instanceof BigDecimal){
            return (T) val;
        }
        if(a instanceof Long){
            Long l = val.longValue();
            return (T) l;
        }
        if(a instanceof Integer){
            Integer i = val.intValue();
            return (T)i;
        }
        if(a instanceof Short){
            Short i = val.shortValue();
            return (T)i;
        }
        return (T)val;
    }

    /**
     * 如果 number 为null则返回 defaultNumber,不为空返回原值
     * @param number
     * @param defaultNumber
     * @return
     */
    public static <T extends Number> T ifNull(Number number,Number defaultNumber){
        return (T) (number==null||number.intValue()==0?defaultNumber:number);
    }


    /**
     * 如果 number 为null则返回 0,不为空返回原值
     * @param number
     * @return
     */
    public static Long ifNullToZeroLong(Long number){
        return (number==null?LONG_ZERO:number);
    }


    /**
     * 如果 number 为null则返回 0,不为空返回原值
     * @param number
     * @return
     */
    public static Long ifNullToZeroLong(Number number){
        return (number==null?LONG_ZERO:convertNumberToTargetClass(number,Long.class));
    }

    /**
     * 如果 number 为null则返回 0,不为空返回原值
     * @param number
     * @return
     */
    public static Integer ifNullToZeroInt(Integer number){
        return (number==null?INTGER_ZERO:number);
    }


    /**
     * 如果 number 为null则返回 0,不为空返回原值
     * @param number
     * @return
     */
    public static Integer ifNullToZeroInt(Number number){
        return (number==null?INTGER_ZERO:convertNumberToTargetClass(number,Integer.class));
    }

    /**
     * 是否为数字 1
     * @param  val
     * @return  true:val=1
     */
    public static boolean isOne(Number val){
        if(val==null)return false;
        if(val instanceof Long){
            return LONG_ONE.equals(val);
        }
        if(val instanceof Integer){
            return INTGER_ONE.equals(val);
        }
        return BigDecimal.ONE.equals(BigDecimal.valueOf(val.doubleValue()));
    }

    //全部为0或者null
    public static boolean allNullOrZero(Number... numbers){
        return !anyNotNullZero(numbers);
    }

    //全部不为0而且不为null
    public static boolean allNotNullZero(Number... numbers){
        return !anyNullOrZero(numbers);
    }


    //任何一个不为0而且不为null
    public static boolean anyNotNullZero(Number... numbers){
        for (Number number : numbers) {
            if(isNotNullOrZero(number)){
                return true;
            }
        }
        return false;
    }

    /**
     * 任何一个为0或者null
     * @param numbers
     * @return
     */
    public static boolean anyNullOrZero(Number... numbers){
        for (Number number : numbers) {
            if(isNullOrZero(number)){
                return true;
            }
        }
        return false;
    }


    /**
     * 不为空而且不为0
     * @return  true:不为空而且不为0
     */
    public static  boolean isNotNullOrZero(Number number){
        if(number==null){
            return false;
        }
        if(number instanceof BigDecimal){
            return BigDecimal.ZERO.compareTo(((BigDecimal) number))!=0;
        }
        if(number instanceof Double){
            return BigDecimal.ZERO.compareTo(BigDecimal.valueOf(number.doubleValue()))!=0;
        }
        if(number instanceof Long){
            return number.longValue()!=0L;
        }
        if(number instanceof Integer){
            return number.intValue()!=0;
        }
        return BigDecimal.ZERO.compareTo( new BigDecimal(number.toString()))!=0;
    }

    //无效的主键
    public static boolean allInvalidUniqueIds(Long[] uniqueIds){
        if (uniqueIds==null) {
            return true;
        }
        boolean b=true;
        for (Long uniqueId : uniqueIds) {
            if (isNotNullOrZero(uniqueId)) {
                b=false;
            }
        }
        return b;
    }

    /**
     * @return  true:为空或者为0
     */
    public static boolean isNullOrZero(Number number){
        return !isNotNullOrZero(number);
    }


    /**
     * 是否小于0
     * @param bigDecimal
     * @return
     */
    public static boolean  isLessZero(BigDecimal bigDecimal){
        if(bigDecimal==null){
            return false;
        }
        if (BigDecimal.ZERO.compareTo(bigDecimal)==1) {
            return true;
        }
        return  false;
    }

    /**
     * 是否小于0
     * @param number
     * @return
     */
    public static boolean  isLessZero(Number number){
        if(number==null){
            return false;
        }
        if(number instanceof Double){
            return BigDecimal.ZERO.compareTo(BigDecimal.valueOf(number.doubleValue()))==1;
        }
        if(number instanceof Long){
            return number.longValue()<0L;
        }
        if(number instanceof Integer){
            return number.intValue()<0;
        }
        return BigDecimal.ZERO.compareTo( new BigDecimal(number.toString()))==1;
    }

    /**
     * 是否大于0
     * @param bigDecimal
     * @return
     */
    public static boolean  isGreaterZero(BigDecimal bigDecimal){
        if(bigDecimal==null){
            return false;
        }
        if (BigDecimal.ZERO.compareTo(bigDecimal)==-1) {
            return true;
        }
        return  false;
    }

    /**
     * 是否大于0
     * @param number
     * @return
     */
    public static boolean  isGreaterZero(Number number){
        if(number==null){
            return false;
        }
        if(number instanceof Double){
            return BigDecimal.ZERO.compareTo(BigDecimal.valueOf(number.doubleValue()))==-1;
        }
        if(number instanceof Long){
            return number.longValue()>0L;
        }
        if(number instanceof Integer){
            return number.intValue()>0;
        }
        return BigDecimal.ZERO.compareTo( new BigDecimal(number.toString()))==-1;
    }


    /**
     * b1是否小于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1<b2  false:b1>=b2}
     */
    public static boolean  isLess(Number b1,Number b2){
        return isLess(CostMathUtils.ifNullToZero(b1),CostMathUtils.ifNullToZero(b2));
    }

    /**
     * b1是否小于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1<b2  false:b1>=b2}
     */
    public static boolean  isLess(BigDecimal b1,BigDecimal b2){
        return compareToNullIsZero(b1,b2)==-1?true:false;
    }

    /**
     * b1是否大于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1>b2  false:b1<=b2}
     */
    public static boolean  isGreater(BigDecimal b1,BigDecimal b2){
        return compareToNullIsZero(b1,b2)==1?true:false;
    }

    /**
     * b1是否大于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1>b2  false:b1<=b2}
     */
    public static boolean  isGreater(Integer b1,Integer b2){
        return compareToNullIsZero(b1,b2)==1?true:false;
    }

    /**
     * b1是否大于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1>b2  false:b1<=b2}
     */
    public static boolean  isGreater(Long b1,Long b2){
        return compareToNullIsZero(b1,b2)==1?true:false;
    }

    //任何一个相同
    public static boolean anyEquals(Integer val1, DictTypeEnumData... vals){
        Integer[] tmpVals = Arrays.stream(vals).map(tmp -> tmp.getValueInteger()).toArray(Integer[]::new);
        return anyEquals(val1,tmpVals);
    }

    //任何一个相同
    public static boolean anyEquals(Integer val1,Integer... vals){
        for (Integer n2 : vals) {
            if (isEquals(val1,n2)) {
                return true;
            }
        }
        return false;
    }

    /**
     * b1是否等于b2,为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code true:b1=b2  false:b1<b2 or b1>b2}
     */
    public static boolean  isEquals(BigDecimal b1,BigDecimal b2){
        return compareToNullIsZero(b1,b2)==0?true:false;
    }

    /**
     * 比较是否相等
     * @param x
     * @param y
     * @return
     */
    public static boolean isEquals(Integer x,Integer y){
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.compareTo(y)==0;
    }

    /**
     * 比较是否相等
     * @param x
     * @param y
     * @return
     */
    public static boolean isEquals(Long x,Long y){
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.compareTo(y)==0;
    }


    /**
     * 四舍五入
     * @param b
     * @return
     */
    public static  BigDecimal toFixed(BigDecimal b){
        return toFixed(b,2);
    }

    /**
     * 四舍五入
     * @param b
     * @param scale  精度
     * @return
     */
    public static  BigDecimal toFixed(BigDecimal b,int scale){
            return new BigDecimal(b.doubleValue()).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }


    /**
     * 判断 b 是否在 start 和 end 之间,默认精度两位
     * @param b
     * @param start     不要大于 end 的值
     * @param end       不要小于 start 的值
     * @return  true: b>=start&&b<=end
     */
    public static boolean betweenAnd(BigDecimal b,BigDecimal start,BigDecimal end){
        return betweenAnd(b,start,end,2);
    }

    /**
     * 判断 b 是否在 start 和 end 之间
     * @param b
     * @param start     不要大于 end 的值
     * @param end       不要小于 start 的值
     * @param scale     比较时使用的小数精度
     * @return  true: b>=start&&b<=end
     */
    public static boolean betweenAnd(BigDecimal b,BigDecimal start,BigDecimal end,int scale){
        BigDecimal startTmp;
        BigDecimal endTmp;
        if (NumberUtils.isGreater(start,end)){
            //如果 大的数在后面
            startTmp=toFixed(end,scale);
            endTmp=toFixed(start,scale);
        }else{
            startTmp=toFixed(start,scale);
            endTmp=toFixed(end,scale);
        }
        BigDecimal bTmp = toFixed(b,scale);
        if(compareToNullIsZero(bTmp,startTmp)==-1){
           return false;
        }
        if(compareToNullIsZero(bTmp,endTmp)==1){
            return false;
        }
        return true;
    }

    /**
     * 比较,如果为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code -1:b1<b2, 0: b1=b2 1:b1>b2 }
     */
    public static int compareToNullIsZero(BigDecimal b1,BigDecimal b2){
        if(b1==b2){
            return 0;
        }
        if(b1==null){
            b1=BigDecimal.ZERO;
        }
        if(b2==null){
            b2=BigDecimal.ZERO;
        }
        return  b1.compareTo(b2);
    }

    /**
     * 比较,如果为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code -1:b1<b2, 0: b1=b2 1:b1>b2 }
     */
    public static int compareToNullIsZero(Long b1,Long b2){
        if(b1==b2){
            return 0;
        }
        if(b1==null){
            b1=0L;
        }
        if(b2==null){
            b2=0L;
        }
        return  b1.compareTo(b2);
    }

    /**
     * 比较,如果为null则当作0处理
     * @param b1
     * @param b2
     * @return   {@code -1:b1<b2, 0: b1=b2 1:b1>b2 }
     */
    public static int compareToNullIsZero(Integer b1,Integer b2){
        if(b1==b2){
            return 0;
        }
        if(b1==null){
            b1=0;
        }
        if(b2==null){
            b2=0;
        }
        return  b1.compareTo(b2);
    }


    /**
     * 字符串转为long集合
     * 逗号作为分隔符
     * @param str
     * @return
     */
    public static List<Long> stringToListLong(String str) {
        return stringToListLong(str, StringUtils.SEPARATOR_COMMA);
    }


    /**
     * 字符串转为long集合
     * @param str
     * @param separator
     * @return
     */
    public static List<Long> stringToListLong(String str, String separator){
        if(StringUtils.isEmpty(str)){
            return new ArrayList<>();
        }
        ArrayList<Long> list = new ArrayList<>();
        String[] split = str.split(separator);
        for (String s : split) {
            try{
                if(StringUtils.isEmpty(s)){
                   continue;
                }
                list.add(Long.parseLong(s));
            }catch (Exception e){
                logger.warn("转换为Number失败",e);
                throw new RuntimeException("转换为Number失败"+s);
            }
        }
        return list;
    }


    public static  Integer hexToIntAuto0xNullIsZero(String s){
        Long l = hexToLongAuto0xNullIsZero(s);
        return l.intValue();
    }

    //十六进制转十进制
    public static  Integer hexToIntAuto0x(String s){
        if (s==null) {
            return null;
        }
        String str =  StringUtils.trim(s).toUpperCase();
        if (str.indexOf("0X")!=-1) {
            str=str.replaceAll("0X","");
        }
        return baseHexToIntNo0x(str);
    }

    public static  Integer hexToIntNo0x(String s){
        return s==null?null:baseHexToIntNo0x(s);
    }

    private static Integer baseHexToIntNo0x(String s){
        return Integer.parseInt(s,16);
    }

    //十六进制转十进制,支持0x前缀,如果为空字符,则做0处理
    public static  Long hexToLongAuto0xNullIsZero(String s){
        Long l = hexToLongAuto0x(s);
        return ifNullToZeroLong(l);
    }

    //十六进制转十进制,支持0x前缀
    public static  Long hexToLongAuto0x(String s){
        if (StringUtils.isEmpty(s)) {
            return null;
        }

        String str =  StringUtils.trim(s).toUpperCase();
        if (str.indexOf("0X")!=-1) {
            str=str.replaceAll("0X","");
        }
        return baseHexToLongNo0x(str);
    }

    /**
     * 十六进制转十进制,不能包含 0x 前缀
     * @param s
     * @return
     */
    public static  Long hexToLongNo0x(String s){
        return s==null?null:baseHexToLongNo0x(s);
    }

    private static Long baseHexToLongNo0x(String s){
        return Long.parseLong(s,16);
    }


    //转换为int
    public static  Integer toInt(Object valve){
        return toInt(valve,null);
    }

    //转换为int
    public static  Integer toInt(Object value,Integer defaultValue){
        return Convert.toInt(value,0);
    }


    //转换为bigDecimal
    public static  BigDecimal toBigDecimal(String s){
        return StringUtils.isEmpty(s)?null:new BigDecimal(s);
    }

    //转换为long
    public static  Long toLong(String s){
       return s==null?null:Long.parseLong(s);
    }

    //转换为long
    public static  Long toLong(Object value){
        return Convert.toLong(value);
    }


    /**
     * 转化为2进制字符串
     * @param bit
     * @return
     */
    public static String toBinaryString(int bit){
        String str = Integer.toBinaryString(bit);
        str=addSplit(str);
        return str;
    }

    public static String toBinaryString(Long bit){
        String str = Long.toBinaryString(bit);
        str=addSplit(str);
        return str;
    }

    public static String addSplit(String str){
        int split=4;
        String s=str;
        StringBuilder sb=new StringBuilder();
        while (true) {
            if (s.length() - split > 0) {
                String tmp = s.substring(s.length() - split);
                if (sb.length() != 0) {
                    sb.insert(0, "_");
                }
                sb.insert(0, tmp);
                s=s.substring(0,s.length() - split);
                continue;
            } else {
                if (sb.length() != 0) {
                    sb.insert(0, "_");
                }
                sb.insert(0, s);
                return sb.toString();
            }

        }
    }


    /**
     * 按时间戳排序,方便进行置顶,如果同时还需根据其它字段进行排序,建议保留为 0 即可,置顶时候才设置该值
     * @return
     */
    public static Long newSortValue(){
        return System.currentTimeMillis()/ DateUtils.MILLIS_PER_SECOND;
    }

    /**
     * <pre>
     * 置顶
     * 排序置顶按照当前时间戳增加一年
     * 保证新添加的不会顶掉之前置顶的排序
     * </pre>
     */
    public static Long newSortTopValue(){
        return (SORT_TOP_VALUE_ADDED+System.currentTimeMillis())/DateUtils.MILLIS_PER_SECOND;
    }


    public static Long newSortValue(Date time){
        return time.getTime()/DateUtils.MILLIS_PER_SECOND;
    }


    /**
     * Convert the given number into an instance of the given target class.
     * @param number the number to convert
     * @param targetClass the target class to convert to
     * @return the converted number
     * @throws IllegalArgumentException if the target class is not supported
     * (i.e. not a standard Number subclass as included in the JDK)
     * @see Byte
     * @see Short
     * @see Integer
     * @see Long
     * @see BigInteger
     * @see Float
     * @see Double
     * @see BigDecimal
     */
    @SuppressWarnings("unchecked")
    public static <T extends Number> T convertNumberToTargetClass(Number number, Class<T> targetClass) {

        if(number == null){
            return null;
        }

        if (targetClass.isInstance(number)) {
            return (T) number;
        }
        else if (Byte.class == targetClass) {
            long value = checkedLongValue(number, targetClass);
            if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
                raiseOverflowException(number, targetClass);
            }
            return (T) Byte.valueOf(number.byteValue());
        }
        else if (Short.class == targetClass) {
            long value = checkedLongValue(number, targetClass);
            if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
                raiseOverflowException(number, targetClass);
            }
            return (T) Short.valueOf(number.shortValue());
        }
        else if (Integer.class == targetClass) {
            long value = checkedLongValue(number, targetClass);
            if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
                raiseOverflowException(number, targetClass);
            }
            return (T) Integer.valueOf(number.intValue());
        }
        else if (Long.class == targetClass) {
            long value = checkedLongValue(number, targetClass);
            return (T) Long.valueOf(value);
        }
        else if (BigInteger.class == targetClass) {
            if (number instanceof BigDecimal) {
                // do not lose precision - use BigDecimal's own conversion
                return (T) ((BigDecimal) number).toBigInteger();
            }
            else {
                // original value is not a Big* number - use standard long conversion
                return (T) BigInteger.valueOf(number.longValue());
            }
        }
        else if (Float.class == targetClass) {
            return (T) Float.valueOf(number.floatValue());
        }
        else if (Double.class == targetClass) {
            return (T) Double.valueOf(number.doubleValue());
        }
        else if (BigDecimal.class == targetClass) {
            // always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double)
            // (see BigDecimal javadoc for details)
            return (T) new BigDecimal(number.toString());
        }
        else {
            throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" +
                    number.getClass().getName() + "] to unsupported target class [" + targetClass.getName() + "]");
        }
    }













    /**
     * Raise an <em>overflow</em> exception for the given number and target class.
     * @param number the number we tried to convert
     * @param targetClass the target class we tried to convert to
     * @throws IllegalArgumentException if there is an overflow
     */
    private static void raiseOverflowException(Number number, Class<?> targetClass) {
        throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" +
                number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow");
    }


    /**
     * Check for a {@code BigInteger}/{@code BigDecimal} long overflow
     * before returning the given number as a long value.
     * @param number the number to convert
     * @param targetClass the target class to convert to
     * @return the long value, if convertible without overflow
     * @throws IllegalArgumentException if there is an overflow
     * @see #raiseOverflowException
     */
    private static long checkedLongValue(Number number, Class<? extends Number> targetClass) {
        BigInteger bigInt = null;
        if (number instanceof BigInteger) {
            bigInt = (BigInteger) number;
        }
        else if (number instanceof BigDecimal) {
            bigInt = ((BigDecimal) number).toBigInteger();
        }
        // Effectively analogous to JDK 8's BigInteger.longValueExact()
        if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) {
            raiseOverflowException(number, targetClass);
        }
        return number.longValue();
    }


    /**
     * 计算百分比,将会直接舍弃小数部分
     * @param targetValue
     * @param currentValue
     * @return
     */
    public static int calcPercentageProgressValue(BigDecimal targetValue,BigDecimal currentValue){
        if (!NumberUtils.isLess(currentValue,targetValue)) {
            return 100;
        }
        BigDecimal bigDecimal = CostMathUtils.divideNullIsZero(CostMathUtils.multiplyNullIsZero(currentValue,100), targetValue);
        return bigDecimal.setScale(0, RoundingMode.DOWN).intValue();
    }

}
